﻿/*

 * Copyright (c) 2013 Diorge Brognara, Renan Peixoto

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

using System;
using System.Text;

namespace Grafos {
    /// <summary>
    /// Representa uma aresta de um grafo qualquer
    /// </summary>
    /// <remarks>
    /// Não há distinção direta as extremidades
    /// Implementações dessa classe podem criar a distinção para arestas direcionadas (dígrafos)
    /// </remarks>
    /// <seealso cref="ArestaPonderada"/>
    /// <example>
    /// Criação de uma nova aresta:
    /// <code>
    /// Aresta a = new Aresta(v1, v2);
    /// v1.Vizinhança.Contains(v2); // retorna true
    /// </code>
    /// </example>
    public class Aresta {

        /// <summary>
        /// Nome da aresta
        /// </summary>
        /// <remarks>
        /// Utilizado somente para melhor visualização
        /// Sendo o valor null ou string.Empty, não será mostrado
        /// </remarks>
        /// <example>
        /// Suponha que uma aresta chame A e conecte os pontos V1 e V2
        /// Sua representação seria da forma:
        /// A: V1=(...) - V2=(...)
        /// Se a aresta não tivesse nome, a representação seria somente:
        /// V1=(...) - V2=(...)
        /// </example>
        public string Nome { get; set; }

        /// <summary>
        /// Retorna uma das extremidades da aresta
        /// </summary>
        /// <remarks>
        /// Nada indica que a extremidade é ponto de "partida" ou "chegada"
        /// Esta classe não implementa dígrafos diretamente
        /// </remarks>
        /// <example>
        /// <code>
        /// Aresta a = new Aresta(v1, v2);
        /// a.Extremidade1.Equals(v1); // retorna true
        /// </code>
        /// </example>
        public Vértice Extremidade1 { get; private set; }

        /// <summary>
        /// Retorna uma das extremidades da aresta
        /// </summary>
        /// <remarks>
        /// Nada indica que a extremidade é ponto de "partida" ou "chegada"
        /// Esta classe não implementa dígrafos diretamente
        /// </remarks>
        /// <example>
        /// <code>
        /// Aresta a = new Aresta(v1, v2);
        /// a.Extremidade2.Equals(v2); // retorna true
        /// </code>
        /// </example>
        public Vértice Extremidade2 { get; private set; }

        /// <summary>
        /// Instancia uma nova aresta, sem nome, a partir de dois vértices
        /// </summary>
        /// <param name="v1">Uma das extremidades da nova aresta</param>
        /// <param name="v2">Uma das extremidades da nova aresta</param>
        /// <example>
        /// <code>
        /// Aresta a = new Aresta(v1, v2);
        /// // equivalente:
        /// Aresta a2 = new Aresta(string.Empty, v1, v2);
        /// </code>
        /// </example>
        /// <exception cref="ArgumentException">Caso V1 ou V2 sejam nulos</exception>
        public Aresta(Vértice v1, Vértice v2)
            : this(string.Empty, v1, v2) {
        }

        /// <summary>
        /// Instancia uma nova aresta, com dado nome, a partir de dois vértices
        /// </summary>
        /// <param name="nome">Nome da aresta</param>
        /// <param name="v1">Uma das extremidades da nova aresta</param>
        /// <param name="v2">Uma das extremidades da nova aresta</param>
        /// <remarks>
        /// A lista de arestas dos vértices é atualizada automaticamente
        /// Se uma aresta for loop (v1 = v2), somente uma aresta é adicionada ao vértice
        /// </remarks>
        /// <example>
        /// <code>
        /// Aresta a = new Aresta("A", v1, v2);
        /// // equivalente:
        /// Aresta a2 = new Aresta(v1, v2);
        /// a2.Nome = "A";
        /// </code>
        /// </example>
        /// <exception cref="ArgumentException">Caso V1 ou V2 sejam nulos</exception>
        public Aresta(string nome, Vértice v1, Vértice v2) {
            Nome = nome;
            Conectar(v1, v2); // arremesa ArgumentException
        }

        /// <summary>
        /// Diz se a aresta é um loop ou não
        /// </summary>
        /// <returns>True caso a aresta seja um loop, false caso contrário</returns>
        /// <remarks>Para que seja verdadeiro, ambas as extremidades tem que ser o mesmo vértice</remarks>
        /// <example>
        /// <code>
        /// // supondo que v1.Equals(v2) seja falso
        /// Aresta a = new Aresta(v1, v2); // a.ÉLoop é falso
        /// Aresta a2 = new Aresta(v1, v1); // a2.ÉLoop é verdadeiro
        /// </code>
        /// </example>
        public bool ÉLoop {
            get {
                return Extremidade1.Equals(Extremidade2);
            }
        }

        /// <summary>
        /// Diz se a aresta é paralela a uma dada aresta
        /// </summary>
        /// <param name="a">Aresta a ser comparada</param>
        /// <returns>True caso sejam paralelas, false caso contrário</returns>
        /// <remarks>
        /// A análise de paralelismo entre arestas parte do princípio de que ambas estejam inseridas no mesmo grafo
        /// Se dois grafos forem criados com os mesmos vértices, as arestas serão paralelas mesmo que não adicionadas ao mesmo grafo
        /// As seguintes propriedades são verdadeiras:
        /// Reflexividade: A1 é paralela a A1
        /// Transitividade: se A1 é paralela a A2, e A2 é paralela a A3, então A3 é paralela a A1
        /// Simétrica: se A1 é paralela a A2, então A2 é paralela a A1
        /// </remarks>
        /// <example>
        /// <code>
        /// Aresta a = new Aresta(v1, v2);
        /// Aresta a2 = new Aresta(v2, v1);
        /// a.ÉParalela(a2); // retorna verdadeiro
        /// </code>
        /// </example>
        public bool ÉParalela(Aresta a) {
            return (this.Extremidade1.Equals(a.Extremidade1) && this.Extremidade2.Equals(a.Extremidade2))
                || (this.Extremidade1.Equals(a.Extremidade2) && this.Extremidade2.Equals(a.Extremidade1));
        }

        /// <summary>
        /// Diz se uma aresta seria paralela a uma aresta que liga dois determinados pontos
        /// </summary>
        /// <param name="v1">Uma das extremidades da suposta aresta</param>
        /// <param name="v2">Uma das extremidades da suposta aresta</param>
        /// <returns>True caso sejam paralelas, false caso contrário</returns>
        /// <remarks>
        /// A análise de paralelismo entre arestas parte do princípio de que ambas estejam inseridas no mesmo grafo
        /// Se dois grafos forem criados com os mesmos vértices, as arestas serão paralelas mesmo que não adicionadas ao mesmo grafo
        /// As seguintes propriedades são verdadeiras:
        /// Reflexividade: A1 é paralela a A1
        /// Transitividade: se A1 é paralela a A2, e A2 é paralela a A3, então A3 é paralela a A1
        /// Simétrica: se A1 é paralela a A2, então A2 é paralela a A1
        /// </remarks>
        /// <example>
        /// <code>
        /// Aresta a = new Aresta(v1, v2);
        /// a.ÉParalela(v2, v1); // retorna true
        /// </code>
        /// </example>
        public bool ÉParalela(Vértice v1, Vértice v2) {
            return (this.Extremidade1.Equals(v1) && this.Extremidade2.Equals(v2))
                || (this.Extremidade1.Equals(v2) && this.Extremidade2.Equals(v1));
        }

        /// <summary>
        /// Gera uma representação textual da aresta
        /// </summary>
        /// <returns>Uma string que contém uma representação em texto desta aresta</returns>
        /// <remarks>
        /// A saída se dá da seguinte forma:
        /// "[Nome]: [V1] - [V2]"
        /// Caso o nome seja nulo:
        /// "[V1] - [V2]"
        /// </remarks>
        /// <seealso cref="Nome"/>
        /// <seealso cref="Vértice.ToString"/>
        public override string ToString() {
            StringBuilder sb = new StringBuilder();
            if (Nome != string.Empty) {
                sb.Append(Nome);
                sb.Append(": ");
            }
            sb.Append(Extremidade1.ToString());
            sb.Append(" - ");
            sb.Append(Extremidade2.ToString());
            return sb.ToString();
        }


        private void Conectar(Vértice v1, Vértice v2) {
            if (v1 == null)
                throw new ArgumentException("Impossível conectar vértices nulos. Parâmetro: v1");
            if (v2 == null)
                throw new ArgumentException("Impossível conectar vértices nulos. Parâmetro: v2");
            Extremidade1 = v1;
            Extremidade2 = v2;
            v1.AdicionarAresta(this);
            if(!v1.Equals(v2))
                v2.AdicionarAresta(this);
        }
    }
}
